// 
// Basic syntax test, this should be translated in/from JooScript 
//
$Unit(__PATH__, __FILE__, function(u,r,g,none)
{
    // metadata will help to figure out of an variable type in the future readings
    "@@@a:#Number;switch:#String;let hashmap1:#Dictionary,hashmap2:#Dictionary,hashmap3:#Dictionary@@@"

    // we will operate in strict context to catch all errors
    "use strict";

    // test null object (in Joo syntax null object is "none")
    undefined, null

    // test context vars

    this.a = 1
    this.test = function (){
        "@@@b:#Number@@@"
        this.b = 2
        // x has obscure namespace, global probably, will bind to unit context after
        // conversion to Joo syntax
        x = 3
        // explicit global variable
        $y = 4
        // static property
        // check type of a because of explicit type
        this.test.z = this.a.__type__ & 2 ? this.a + this.b + x + $y : u.typeError(123)
    }
    this.test()

    // some JS keywords can be used as identifiers
    this["switch"] = "foo"

    /* var ns = Namespace() */ // TODO

    // test roles

    // role fooRole {...}
    this.fooRole = this.Role({
        foo : this.Type.NUMBER,
        bar : {
            type: u.Type.FUNCTION,
            hidden: true
        },
        baz : null,
        doc : XML
    })

    this.barRole = Role()

    // fixme a.b.c.
    u.Package("a.b.c", function(grp){

        var Foo = u.buildClass("Foo",null,null,unit.fooRole)
        var Bar = u.buildClass("Bar",[u.A], function(){}, unit.barRole)
        
        // export class ABC
        grp.ABC = u.buildClass( // build class
                "ABC",  // class name
                [u.A, r.B, g.C],   // ancestors
                function(cls){ // class body
                    //
                    // actual class constructor
                    this["new"] = function(self){ 
                    }
                    this.foo = function(self, a, b, rest){ 
                        self.x = rest[0]
                        this.y = rest[1]
                    }
                    this.bar = function(self, a, b, c, d){ 
                    }
                    // lambda expression
                    this.lambda = function(self, a, b){ 
                        return a + b
                    }
                    this.puk = 1234
                    this.pin = 5678
                    // no roles for code, it will not touched
                    this.code = this.pin + this.puk
                    // list
                    this.a = 1, this.b = 2, this.c = 3
                    // static
                    cls.onetwothree = this.a + this.b + this.c
                },{ // class spec (role)
                    "new" : {
                        type: u.Type.FUNCTION,
                        constructor: true,
                        // it returns none or everyting
                        returnType: Type.NONE
                    },
                    foo : {
                        type: u.Type.FUNCTION,
                        argumentType: [u.Type.STRING, Bar],
                        returnType: Foo,
                        restArgument: true,
                        hidden: true,
                        frozen: true,
                        sealed: true
                        /* dictArgument: true */ // TODO
                    },
                    bar : {
                        type: "Function",
                        // fixme argumentType: ["Uint", Foo, , "String"],
                        argumentType: [u.Type.UINT, Foo, null, u.Type.STRING],
                        defaultArguments: [123, "abc"],
                        returnType: Bar,
                        internal: true
                    },
                    lambda : "Function",
                    puk : "Number",
                    pin : {
                        type: u.Type.INT,
                        frozen: true 
                    }
                })

        // export some string
        grp.foo = "foo"

        var abc = this.ABC()

        // and function ...
        this.bar = function(){
            return grp.foo + abc.code
        }

        // unexported
        this.baz = 123
    }, { 
        // exports
        ABC : u.Type.CLASS,
        foo : u.Type.STRING,
        bar : u.Type.FUNCTION
    });

    var test = $buildClass(null, null, null)

    function JSClass() {
    }

    var foo = new JSClass
    var bar = new JSClass("abc")

    // in jooscript "!", "&&" and "||" is "not", "and" and "or" respectively
    if( a && b || !c )
        test()
    else {
        // empty
    }

    // lambda expression

    this.lambda = function(x) {
        return x*x
    }

    // in jooscript it should be: 
    // for i in range(10) ...
    for(this.i=0; this.i>10; this.i++){
        print(this.i)
    }

    // for let ĩ in range(10, 20) ...
    {
        "@@@for let ĩ:$entu@@@"    
        var $entu
        for($entu=10; $entu>20; $entu++){
            print($entu)
        }
    }

    // for ī in range(-10, -30, -3) ...
    for(this.ī=-10; this.ī>-30; this.ī+=-3){
        print(this.ī)
    }

    // test import 

    this.Import("a.b.c.*")

    // test events syntax

    // when "initialize" do ...
    this.Event.when("initialize", function(e){
        var t = e.type
    })

    // once "contentloaded" do ...
    $Event.once("contentloaded", oncontentloaded)

    // dispatch "eventname"
    $Event.dispatch("eventname")

    // dispatch "eventname" to foo.bar
    $Event.dispatch("eventname", foo.bar)

    // remove "eventname"
    $Event.dismiss("eventname")
    
    // remove "eventname" from foo.bar
    $Event.dismiss("eventname", foo.bar)

    // use capture, when "click" on window do ...
    window.addEventListener("click", function(e){
        var t = e.type
    }, true)

    // when "mousemove" over foo.bar do ...
    foo.bar.addEventListener("mousemove", function(e){
        var t = e.type
    }, false)
    
    // when "message" from worker do ...
    worker.addEventListener("message", onmessage, false)

    /* TODO */
    /* $Event.when("mousemove", element, function(e){}) */ 
    /* $Event.dispatch({type: "mousemove", x: 123, y: 456}, element) */
    /* $Event.remove(<event>, element) */

    // test hashes

    // hashmap1 = (foo => 123, "bar" => "abc")
    this.hashmap1 = this.$dict([[foo, 123], ["bar", "abc"]])
    // let hashmap2 = ("foo", "bar", 123 => "baz")
    var hashmap2 = u.$dict([[0, "foo"], [1, "bar"], [123, "baz"]])
    // hashmap3 = ("foo", )
    this.hashmap3 = u.$dict([[0, "foo"], ])
    // hashtest = hashmap1["bar"]
    this.foo = this.hashmap1.get("bar")
    // hashmap1[foo] = "bar"
    this.hashmap1.set(foo, "bar")
    // hashmap2[foo] = "bar"
    hashmap2.set(foo, "bar")
    // let bar = hashmap2.get("foo")
    var bar = hashmap2.get("foo")
    
    // test for each loop for dictonary

    // for each foo in hashmap1 { trace(foo) }
    {
        "@@@for each foo:$fhg7,$rng9:#Dictionary@@@"
        var $fhg7, $rng9=this.hashmap1, $df4g=0;
        for( this.foo=$rng9.valueAt($df4g); 
                 $df4g<$rng9.length;
                 this.foo=$rng9.valueAt(++$df4g)) 
        {
             trace(this.foo)
        }
    }

    // for each let bar in hashmap2 { trace(bar) }
    {
        "@@@for each let bar:$nth4,$4hg8:#Dictionary@@@"
        var $nth4, $4hg8=hashmap2, $ht5d=0;
        for( $nth4=$4hg8.valueAt($ht5d); 
                 $ht5d<$4hg8.length;
                 $nth4=$4hg8.valueAt(++$ht5d)) 
        {
             trace($nth4)
        }
    }

    // iterate on simple objects

    // for each let baz in {a: 1, b: 2} { trace(bar) }
    {
        "@@@for each let baz:$jqpa,$hrwq:#Object@@@"
        var $jqpa, $hrwq={a: 1, b: 2};
        for( $jqpa in $hrwq ) 
        {
             trace($hrwq[$jqpa])
        }
    }

    // block scope test

    // { let foo Int = 1, bar = "abc" }
    {
        "@@@let foo:$nhir#Int,bar:$yv12@@@"
        var $nhir = 1, $yv12 = "abc"
    }

    // with(ctx) moveTo(0,1), lineTo(1,2), bezierCurveTo(2,3)
    {
        "@@@with_list $oqu3@@@"
        var $oqu3 = ctx
        $oqu3.moveTo(0,1), $oqu3.lineTo(1,2), $oqu3.bezierCurveTo(2,3)
    }

    // with(bar) { let foo = "abc"; pop(); push(foo) }
    {
        "@@@with_block $g4u1;let foo:$ntp1@@@"
        var $g4u1 = bar
        var $ntp1 = "abc"
        $g4u1.pop()
        $g4u1.push($ntp1)
    }

    // strict equality operator is deprecated in favor of "is" keyword

    foo === bar

})
